
/* GCSx
** ANIMGROUPPAINT.CPP
**
** Animation group editing
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#include "all.h"

// Tool panel

const ToolSelect::ToolIconStruct AnimGroupPaintTools::toolIcons[TOOLSELECT_NUM_TOOLS] = {
    { TOOLS_PLACEITEM, 80, 100, "Place components" },
    { TOOLS_SELECTITEM, 0, 120, "Select by component" },
    { TOOLS_SELECT, 0, 20, "Select via rectangle" },
};

const ToolSelect::ToolIconStruct AnimGroupPaintTools::outlineIcons[TOOLSELECT_NUM_OUTLINE] = {
    { VIEW_OUTLINE_NONE, 20, 120, "Don't show outlines" },
    { VIEW_OUTLINE_NORMAL, 40, 120, "Show outlines on components" },
    { VIEW_OUTLINE_HANDLE, 60, 120, "Show outlines with handles" },
};

int AnimGroupPaintTools::event(int hasFocus, const SDL_Event* event) { start_func
    if (event->type == SDL_COMMAND) {
        // Choose tool or dots
        int newToolL = 0;
        int newToolR = 0;
        int newOutline = 0;
        WButton* toolButtonL = dynamic_cast<WButton*>(findWidget(ID_TOOLL));
        WButton* toolButtonR = dynamic_cast<WButton*>(findWidget(ID_TOOLR));
        WButton* outlineButton = dynamic_cast<WButton*>(findWidget(ID_OUTLINE));
        int x, y;
        
        if (event->user.code == TOOLS_CHOOSE) {
            newToolL = toolSelect->run(toolButtonL->getScreenX(), toolButtonL->getScreenY() + toolButtonL->getHeight());
        }
        else if (event->user.code == TOOLS_CHOOSER) {
            newToolR = toolSelect->run(toolButtonR->getScreenX(), toolButtonR->getScreenY() + toolButtonR->getHeight());
        }
        else if (toolSelect->isTool(event->user.code)) {
            newToolL = event->user.code;
        }
        else if (event->user.code == VIEW_OUTLINE_CHOOSE) {
            newOutline = outlineSelect->run(outlineButton->getScreenX(), outlineButton->getScreenY() + outlineButton->getHeight());
        }
        else if (outlineSelect->isTool(event->user.code)) {
            newOutline = event->user.code;
        }
        
        if (newToolL) {
            if (toolSelect->getToolIcon(newToolL, x, y)) {
                toolButtonL->changeIcon(x, y);
                *toolLPtr = newToolL;
                return 1;
            }
        }
        
        if (newToolR) {
            if (toolSelect->getToolIcon(newToolR, x, y)) {
                toolButtonR->changeIcon(x, y);
                *toolRPtr = newToolR;
                return 1;
            }
        }

        if (newOutline) {
            if (outlineSelect->getToolIcon(newOutline, x, y)) {
                outlineButton->changeIcon(x, y);
                *outlinePtr = newOutline;
                paintArea->refresh();
                config->write(ANIMEDIT_OUTLINE, *outlinePtr);
                return 1;
            }
        }
        
        if ((event->user.code == TOOLS_CHOOSE) || (event->user.code == TOOLS_CHOOSER) || (event->user.code == VIEW_OUTLINE_CHOOSE)) return 1;
    }

    return Dialog::event(hasFocus, event);
}

void AnimGroupPaintTools::childModified(Window* modified) { start_func
    Dialog::childModified(modified);

    // @TODO: detect changes in animation name
    // @TODO: selection in listbox should update tilesetPtr

    // [outline setting updates all handled in event()]
}

void AnimGroupPaintTools::previewRedraw() { start_func
    // @TODO: preview isn't added yet : assert(findWidget(ID_PREVIEW));
    // (doesn't throw exceptions)
    // @TODO: preview isn't added yet : findWidget(ID_PREVIEW)->load();
}

int AnimGroupPaintTools::wantsToBeDeleted() const { start_func
    return 1;
}

FrameWindow* AnimGroupPaintTools::createWindowed() { start_func
    // Prevent duplication
    if (myFrame) {
        return myFrame;
    }

    // We remember the frame pointer even though it'll delete itself
    myFrame = new FrameWindow("Tools", FrameWindow::RESIZING_SNAP, FrameWindow::FRAMETYPE_DIALOG, this, FrameWindow::TITLEBAR_TOOL, 0);
    return myFrame;
}

Window::WindowSort AnimGroupPaintTools::windowSort() const { start_func
    return WINDOWSORT_ONTOP;
}

AnimGroupPaintTools::AnimGroupPaintTools(AnimGroupPaint* tPaintArea, ImageSelect* mImagebar, int* toolL, int* toolR, int* outline, string* animName, TileSetEdit** tileset) : Dialog(blankString, 1, 0, 1) { start_func
    myFrame = NULL;
    imagebar = mImagebar;
    paintArea = tPaintArea;
    toolLPtr = toolL;
    toolRPtr = toolR;
    outlinePtr = outline;
    animNamePtr = animName;
    tilesetPtr = tileset;

    Widget* w = NULL;
    
    toolSelect = new ToolSelect(getIconSurface(), TOOLSELECT_NUM_TOOLS, toolIcons);
    outlineSelect = new ToolSelect(getIconSurface(), TOOLSELECT_NUM_OUTLINE, outlineIcons);

    int x = 0;
    int y = 0;
    toolSelect->getToolIcon(*toolLPtr, x, y);

    w = new WButton(ID_TOOLL, getIconSurface(), x, y, 20, 20, Dialog::BUTTON_NOTHING, TOOLS_CHOOSE);
    w->setToolTip("Change tool (left button)");
    w->addTo(this);

    x = y = 0;
    toolSelect->getToolIcon(*toolRPtr, x, y);

    w = new WButton(ID_TOOLR, getIconSurface(), x, y, 20, 20, Dialog::BUTTON_NOTHING, TOOLS_CHOOSER);
    w->setToolTip("Change tool (right button)");
    w->addTo(this);

    arrangeRow();

    w = new WButton(ID_COPY, getIconSurface(), 60, 20, 20, 20, Dialog::BUTTON_NOTHING, EDIT_COPY);
    w->setToolTip("Copy");
    w->addTo(this);
    
    w = new WButton(ID_PASTE, getIconSurface(), 80, 20, 20, 20, Dialog::BUTTON_NOTHING, EDIT_PASTE);
    w->setToolTip("Paste");
    w->addTo(this);
    
    outlineSelect->getToolIcon(*outlinePtr, x, y);
    w = new WButton(ID_OUTLINE, getIconSurface(), x, y, 20, 20, Dialog::BUTTON_NOTHING, VIEW_OUTLINE_CHOOSE);
    w->setToolTip("Control display of component outlines");
    w->addTo(this);
    
    arrangeRow();

    // @TODO: disable/enable these buttons dynamically?
    w = new WButton(ID_TOP, getIconSurface(), 40, 140, 20, 20, Dialog::BUTTON_NOTHING, EDIT_MOVETOP);
    w->setToolTip("Move component to top (front)");
    w->addTo(this);
    
    w = new WButton(ID_UP, getIconSurface(), 0, 140, 20, 20, Dialog::BUTTON_NOTHING, EDIT_MOVEUP);
    w->setToolTip("Move component up (towards front)");
    w->addTo(this);
    
    w = new WButton(ID_DOWN, getIconSurface(), 20, 140, 20, 20, Dialog::BUTTON_NOTHING, EDIT_MOVEDOWN);
    w->setToolTip("Move component down (towards back)");
    w->addTo(this);
    
    w = new WButton(ID_BOTTOM, getIconSurface(), 60, 140, 20, 20, Dialog::BUTTON_NOTHING, EDIT_MOVEBOTTOM);
    w->setToolTip("Move component to bottom (back)");
    w->addTo(this);
    
    arrangeRow();
    
    w = new WStatic(ID_NAMELABEL, "Animation name:");
    w->addTo(this);
    
    arrangeRow();

    w = new WTextBox(ID_NAME, animNamePtr, 0);
    w->setToolTip("Provide a descriptive name for your sprite animation");
    w->addTo(this);
    
    arrangeRow();

    w = new WStatic(ID_TILESETLABEL, "Image/Tile set:");
    w->addTo(this);
    
    arrangeRow();

    w = tilesetList = new WListBox(ID_TILESET, &selectedTileset);
    w->setToolTip("Select which image/tile set to use for new components");
    w->addTo(this);
    
    arrangeRow();

    // @TODO:
    // Request initial height based on @TODO:, although this will inevitably be resized
    int previewHeight = 96;
//        if (*tileSource) previewHeight = (*tileSource)->h * 3;
//        w = new WPreview(ID_PREVIEW, tileSource, 166, previewHeight, 1, 0);
//        w->setToolTip("Preview");
//        w->addTo(this);
//        w = NULL;
//        arrangeRow();
    
    // Our height if preview is 0 pixels high
    minHeight = height - previewHeight;

    runAsPanel();
}

AnimGroupPaintTools::~AnimGroupPaintTools() { start_func
    delete toolSelect;
    delete outlineSelect;
}

void AnimGroupPaintTools::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
    if (newViewHeight == -1) newViewHeight = viewWidth;
    
    if (newViewHeight >= 0) {
        // Resize animation preview to fit
        // @TODO:
//        WPreview* widget = dynamic_cast<WPreview*>(findWidget(ID_PREVIEW));
//        if (widget) { start_func
//            int previewHeight = newViewHeight - minHeight;
//            if (previewHeight > 0) { start_func
//                widget->changeSize(166, previewHeight);
//                // Doesn't throw exceptions
//                widget->load();
//                newHeight = newViewHeight;
//            }
//            else {
//                widget->changeSize(166, 0);
//                // Doesn't throw exceptions
//                widget->load();
//                newHeight = minHeight;
//            }
//        }
    }

    Window::resize(newWidth, newHeight, newViewWidth, newViewHeight, fromParent);
}

// Main edit window

AnimGroupPaint::AnimGroupPaint(AnimGroupEdit* myAnimGroup, int animId) throw_File : Window(), anim(), selected(), backupSelected() { start_func
    assert(myAnimGroup);
    // @TODO: assert imageId is valid

    haveFocus = partialFocus = 0;
    toolActive = 0;
    cursorComp = -1;
    seqId = animId;
    frameId = 0;
    frameCount = 1;
    selectRect.w = 0;

    animgroup = myAnimGroup;

    // Intentionally doesn't remember last tool selected
    toolL = TOOLS_PLACEITEM;
    toolR = TOOLS_SELECTITEM;
    // Recall last settings
    outline = config->readNum(ANIMEDIT_OUTLINE);
    if ((outline != VIEW_OUTLINE_NONE) && (outline != VIEW_OUTLINE_HANDLE)) outline = VIEW_OUTLINE_NORMAL;
    // @TODO: store setting and also, this setting isn't used yet
    enableGrid = 0;

    myFrame = NULL;
    tools = NULL;
    imagebar = NULL;
    colorbar = NULL;
    animgroup->markLock(); // Exception point

    FrameWindow* toolsFrame = NULL;
    FrameWindow* imagebarFrame = NULL;
    FrameWindow* colorbarFrame = NULL;

    try {
        myFrame = new FrameWindow(blankString, FrameWindow::RESIZING_NORMAL, FrameWindow::FRAMETYPE_BEVEL_BK, this);
        myFrame->setAutoCenter(1);

        refreshData();
        refreshName();
        
        // Get tileset to start with
        assert(anim.count); // @TODO: if no frames, add one?
        if (anim.frames[frameId].count) {
            tileset = dynamic_cast<TileSetEdit*>(anim.frames[frameId].comps[0].tileset);
        }
        else {
            // @TODO: if no tilesets, ?
            World::TileSetIndex::const_iterator firstTS = animgroup->getWorldEdit()->beginTileSet();
            assert(firstTS != animgroup->getWorldEdit()->endTileSet());
            tileset = dynamic_cast<TileSetEdit*>((*firstTS).second);
        }
        
        imagebar = new ImageSelect(tileset, &colors, 0); // One exeption point (others?)
        colorbar = new ColorSelect(&colors, 0, 0);
        tools = new AnimGroupPaintTools(this, imagebar, &toolL, &toolR, &outline, &anim.name, &tileset);
        
        imagebarFrame = imagebar->createWindowed();
        colorbarFrame = colorbar->createWindowed();
        toolsFrame = tools->createWindowed();
        
        updateTitlebar();
        myFrame->addToolPanel(imagebarFrame, FrameWindow::CLIENT_BOTTOM);
        myFrame->addToolPanel(colorbarFrame, FrameWindow::CLIENT_RIGHT);
        myFrame->addToolPanel(toolsFrame, FrameWindow::CLIENT_RIGHT);
        myFrame->show(FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CURRENT, FrameWindow::SHOW_CURRENT);
        imagebar->colorRefresh();
    }
    catch (...) {
        if (myFrame) {
            myFrame->dropClients();
            delete myFrame;
            myFrame = NULL;
        }
        if (toolsFrame) {
            delete toolsFrame;
        }
        else {
            delete tools;
        }
        if (colorbarFrame) {
            delete colorbarFrame;
        }
        else {
            delete colorbar;
        }
        if (imagebarFrame) {
            delete imagebarFrame;
        }
        else {
            delete imagebar;
        }
        animgroup->markUnlock();
        throw;
    }

    // Disabled/etc status on various tools
    tools->findWidget(AnimGroupPaintTools::ID_COPY)->disable();
}

AnimGroupPaint::~AnimGroupPaint() { start_func
    if (animgroup) animgroup->markUnlock();
}

void AnimGroupPaint::siblingModified(Window* modified) { start_func
    // Img selector updates with any color change
    if (modified == colorbar) {
        if (imagebar) imagebar->colorRefresh();
    }
}

void AnimGroupPaint::setDirtyRect(const Rect& rect) { start_func
    // Clip
    Rect bound = { 0, 0, anim.width, anim.height };
    if (intersectRects(bound, rect)) {
        // Add rectangle into dirty range
        boundRects(dirtyRange, bound);
        setDirty();
    }
}

void AnimGroupPaint::setDirtyBox(int x1, int y1, int x2, int y2) { start_func
    // Create a rectangle
    // Add rectangle into dirty range
    setDirtyRect(createRect(x1, y1, x2, y2));
}

void AnimGroupPaint::setDirtyComp(int subid) { start_func
    assert(subid >= 0);
    assert(subid < anim.frames[frameId].count);
    
    Rect rect = { anim.frames[frameId].comps[subid].x, anim.frames[frameId].comps[subid].y,
                      anim.frames[frameId].comps[subid].tileset->getWidth(),
                      anim.frames[frameId].comps[subid].tileset->getHeight() };

    setDirtyRect(rect);
}

void AnimGroupPaint::refreshData() { start_func
    assert(animgroup);

    animgroup->loadAnim(seqId, &anim);
    resize(anim.width + RESIZE_HANDLE_SIZE, anim.height + RESIZE_HANDLE_SIZE);
    
    // @TODO: lock/unlock tilesets used by animation
}

void AnimGroupPaint::applyData() { start_func
    assert(animgroup);

    animgroup->saveAnim(seqId, &anim, this);
    tools->previewRedraw();
}

void AnimGroupPaint::refreshName() { start_func
    assert(animgroup);

    animgroup->loadAnimName(seqId, &anim.name);
}

void AnimGroupPaint::applyName() { start_func
    assert(animgroup);

    animgroup->saveAnimName(seqId, &anim.name, this);
}

// @TODO: call on changes
void AnimGroupPaint::updateTitlebar() { start_func
    if ((myFrame) && (animgroup)) {
        myFrame->setTitle(formatString("%s : %s : Anim. %s", animgroup->getWorld()->getTitle().c_str(), animgroup->getName().c_str(), anim.name.c_str()));
    }
}

int AnimGroupPaint::event(int hasFocus, const SDL_Event* event) { start_func
    int changed = 0;
    ObjChange* obj;

    switch (event->type) {
        case SDL_CLOSE:
            return 1;
    
        case SDL_SPECIAL:
            // Refresh selection rectangle?
            if ((event->user.code == SDL_IDLEPHASE) && (partialFocus) && (selectRect.w)) {
                setDirtyRect(selectRect);
            }
            // Refresh cursor?
            if (((event->user.code == SDL_IDLECURSOR) || (event->user.code == SDL_IDLEPHASE)) && (haveFocus) && (cursorComp >= 0)) {
                setDirtyComp(cursorComp);
            }
            return 1;

        case SDL_COMMAND:
            switch (event->user.code) {
                case EDIT_UNDO:
                    // finish instead of cancel- this causes undo to undo current
                    // tool if one is being used
                    finishTool();
                    // (let world handle from here)
                    break;
                    
                case EDIT_REDO:
                    cancelTool();
                    // (let world handle from here)
                    break;

                case EDIT_COPY:
                    copySelection();
                    return 1;
                    
                case EDIT_CUT:
                    try { // @TODO: Try block not technically needed until undo functions work
                        animgroup->getWorldEdit()->undo.preUndoBlock();
                        copySelection();
                        deleteSelection();
                        clearSelection();
                        animgroup->getWorldEdit()->undo.postUndoBlock();
                    }
                    catch (UndoException& e) {
                    }
                    return 1;
                    
                case EDIT_DELETE:
                    try { // @TODO: Try block not technically needed until undo functions work
                        deleteSelection();
                    }
                    catch (UndoException& e) {
                    }
                    return 1;
                    
                case EDIT_SELECTALL:
                    try { // @TODO: Try block not technically needed until undo functions work
                        animgroup->getWorldEdit()->undo.preUndoBlock();
                        undoStoreSelect();
                        animgroup->getWorldEdit()->undo.postUndoBlock();

                        // Select all
                        // @TODO:
                        setDirty(1);
                        tools->findWidget(AnimGroupPaintTools::ID_COPY)->enable();
                    }
                    catch (UndoException& e) {
                    }

                    return 1;
                    
                case EDIT_DESELECTALL:
                    cancelTool();
                    try { // @TODO: Try block not technically needed until undo functions work
                        clearSelection();
                    }
                    catch (UndoException& e) {
                    }
                    return 1;
                    
                case EDIT_PASTE:
                    try { // @TODO: Try block not technically needed until undo functions work
                        pasteSelection();
                    }
                    catch (UndoException& e) {
                    }
                    
                    // Make sure a selection tool is current
                    if ((!toolActive) && (toolL != TOOLS_SELECT) && (toolL != TOOLS_SELECTITEM)) {
                        desktop->broadcastEvent(SDL_COMMAND, TOOLS_SELECTITEM);
                        desktop->broadcastEvent(SDL_COMMAND, CMD_RELEASE);
                    }
                    
                    return 1;
                    
                case CMD_RELEASE:
                    finishTool(); // safe even if no tool
                    return 1;
                    
                case TOOLS_NEXTCOLOR:
                    colorbar->colorSelection(ColorSelect::SELECTED_FG,
                                             colorbar->colorSelection(ColorSelect::SELECTED_FG) + 1);
                    return 1;

                case TOOLS_PREVCOLOR:
                    colorbar->colorSelection(ColorSelect::SELECTED_FG,
                                             colorbar->colorSelection(ColorSelect::SELECTED_FG) - 1);
                    return 1;

                case TOOLS_EDITCOLOR:
                    colorbar->editColor(colorbar->colorSelection(ColorSelect::SELECTED_FG));
                    return 1;
            }
            
            if (animgroup->getWorldEdit()->commandEvent(event->user.code)) return 1;
            break;

        case SDL_OBJECTCHANGE:
            obj = (ObjChange*)event->user.data1;
            if (event->user.code & OBJ_TILESET) {
                // @TODO:
            }
            if (event->user.code & OBJ_ANIMGROUP) {
                // @TODO:
            }
            if ((event->user.code & OBJ_WORLD) && (obj->obj == animgroup->getWorld())) {
                if (event->user.code & OBJMOD_DELETE) {
                    imagebar->changeSet(NULL, 1);
                    animgroup = NULL;
                    closeWindow();
                }
                if (event->user.code & OBJMOD_NAME) {
                    updateTitlebar();
                }
            }
            return 1;

        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONDBL:
            if ((event->button.button == SDL_BUTTON_LEFT) || (event->button.button == SDL_BUTTON_RIGHT)) {
                // Signed as it could go off the edge
                Sint16 cX = event->button.x;
                Sint16 cY = event->button.y;
                // If unmodified selection-tool left-click and within selection, this is a selection drag
                if ((!toolActive) &&
                    ((toolL == TOOLS_SELECT) || (toolL == TOOLS_SELECTITEM)) &&
                    !(SDL_GetModState() & (KMOD_CTRL | KMOD_ALT)) &&
                    (event->button.button == SDL_BUTTON_LEFT) &&
                    (isInSelection(cX, cY))) {
                    
                    startToolSelectionDrag(cX, cY);
                }
                else {
                    startTool(cX, cY, event->button.button);
                }
                return 1;
            }
            break;
    
        case SDL_MOUSEBUTTONUP:
            finishTool();
            return 1;
    
        case SDL_MOUSEMOTION:
            if ((event->motion.state & SDL_BUTTON_LMASK) || (event->motion.state & SDL_BUTTON_RMASK)) {
                // Signed as it could go off the edge
                dragTool((Sint16)event->motion.x, (Sint16)event->motion.y);
            }
            else {
                // Update mouse pointer
                mousePointer((Sint16)event->motion.x, (Sint16)event->motion.y);
            }
            return 1;

        case SDL_MOUSEFOCUS:
            if (event->user.code & 1) {
                hover = 1;
                mousePointer();
            }
            else {
                hover = 0;
                selectMouse(MOUSE_NORMAL);
            }
            return 1;
        
        case SDL_INPUTFOCUS:
            if (event->user.code & 1) {
                if (!haveFocus) {
                    haveFocus = partialFocus = 1;
                    changed = 1;
                }
            }
            else if (event->user.code & 2) {
                if (!partialFocus) {
                    partialFocus = 1;
                    changed = 1;
                }
            }
            else {
                if (partialFocus) {
                    partialFocus = 0;
                    changed = 1;
                }
            }
            if (!(event->user.code & 1)) {
                if (haveFocus) {
                    cancelTool();
                    haveFocus = 0;
                    changed = 1;
                }
            }
            
            if (changed) {
                // Refresh selection rectangle and cursor
                setDirtyRect(selectRect);
                if (cursorComp >= 0) setDirtyComp(cursorComp);
            }
            return 1;

        case SDL_KEYUP:
            switch (event->key.keysym.sym) {
                case SDLK_LALT:
                case SDLK_RALT:
                case SDLK_LCTRL:
                case SDLK_RCTRL:
                    modifyTool();
                    return 1;
                
                default:
                    break;
            }
            break;

        case SDL_KEYDOWN:
            switch (event->key.keysym.sym) {
                case SDLK_TAB:
                    // @TODO: change cursor/selection; detect shift
                    break;

                case SDLK_LALT:
                case SDLK_RALT:
                case SDLK_LCTRL:
                case SDLK_RCTRL:
                    modifyTool();
                    return 1;

                case SDLK_RIGHT:
                    // @TODO: undo and store for all key-based selection movement
                    if (event->key.keysym.mod & KMOD_CTRL) moveSelection(anim.width - selectRect.w - selectRect.x, 0);
                    else moveSelection(1, 0);
                    return 1;
                        
                case SDLK_END:
                    if (event->key.keysym.mod & KMOD_CTRL) moveSelection(anim.width - selectRect.w - selectRect.x, anim.height - selectRect.h - selectRect.y);
                    else moveSelection(anim.width - selectRect.w - selectRect.x, 0);
                    return 1;
                        
                case SDLK_LEFT:
                    if (event->key.keysym.mod & KMOD_CTRL) moveSelection(-selectRect.x, 0);
                    else moveSelection(-1, 0);
                    return 1;

                case SDLK_HOME:
                    if (event->key.keysym.mod & KMOD_CTRL) moveSelection(-selectRect.x, -selectRect.y);
                    else moveSelection(-selectRect.x, 0);
                    return 1;
                        
                case SDLK_DOWN:
                    if (event->key.keysym.mod & KMOD_CTRL) moveSelection(0, anim.height - selectRect.h - selectRect.y);
                    else moveSelection(0, 1);
                    return 1;

                case SDLK_UP:
                    if (event->key.keysym.mod & KMOD_CTRL) moveSelection(0, -selectRect.y);
                    else moveSelection(0, -1);
                    return 1;

                case SDLK_PAGEDOWN:
                    moveSelection(0, anim.height - selectRect.h - selectRect.y);
                    return 1;

                case SDLK_PAGEUP:
                    moveSelection(0, -selectRect.y);
                    return 1;
                
                default:
                    break;
            }
            break;
    }

    return 0;
}

void AnimGroupPaint::moveSelection(int changeX, int changeY) { start_func
    // Ensure a selection exists and we're actually moving it
    if ((selectRect.w) && ((changeX) || (changeY))) {
        // Dirty old
        setDirtyRect(selectRect);
        
        // Clip movement
        if (selectRect.x + changeX < 0) changeX = -selectRect.x;
        if (selectRect.y + changeY < 0) changeY = -selectRect.y;
        if (selectRect.x + changeX + selectRect.w > anim.width) changeX = anim.width - selectRect.w - selectRect.x;
        if (selectRect.y + changeY + selectRect.h > anim.height) changeY = anim.height - selectRect.h - selectRect.y;
        
        // Move all selected components
        set<int>::iterator end = selected.end();
        for (set<int>::iterator pos = selected.begin(); pos != end; ++pos) {
            assert(*pos >= 0);
            assert(*pos < anim.count);
            anim.frames[frameId].comps[*pos].x += changeX;
            anim.frames[frameId].comps[*pos].y += changeY;
        }
        
        // Modify rect
        selectRect.x += changeX;
        selectRect.y += changeY;
        
        // Dirty new
        setDirtyRect(selectRect);
        
        // @TODO: scroll to view selection
    }
}

void AnimGroupPaint::startToolSelectionDrag(int x, int y) { start_func
    if (toolActive) return;

    toolActive = TOOLS_SELECTDRAG;
    toolLastX = toolStartX = x;
    toolLastY = toolStartY = y;
    toolCtrl = SDL_GetModState() & KMOD_CTRL;
    toolAlt = SDL_GetModState() & KMOD_ALT;
    mousePointer();
    dragTool(x, y, 1);
}

void AnimGroupPaint::startTool(int x, int y, int button) { start_func
    if (toolActive) return;

    toolActive = button == SDL_BUTTON_LEFT ? toolL : toolR;
    toolLastX = toolStartX = x;
    toolLastY = toolStartY = y;
    toolCtrl = SDL_GetModState() & KMOD_CTRL;
    toolAlt = SDL_GetModState() & KMOD_ALT;
    mousePointer();
    dragTool(x, y, 1);
}


void AnimGroupPaint::modifyTool() { start_func
    if (toolActive) {
        toolCtrl = SDL_GetModState() & KMOD_CTRL;
        toolAlt = SDL_GetModState() & KMOD_ALT;

        // Selection tools need entire area dirtied because this may cause
        // a deleted selection to reappear
        if ((toolActive == TOOLS_SELECT) || (toolActive == TOOLS_SELECTITEM)) {
            setDirty(1);
        }

        dragTool(toolLastX, toolLastY);
    }

    // Always update mouse pointer even if no active tool
    mousePointer();
}

void AnimGroupPaint::dragTool(int x, int y, int firstTime, int lastTime) { start_func
    if (toolActive) {
// @TODO:
//        int rX, rY;
//        int slope1, slope2;
//        Rect rect;
//        Rect layerBound = { 0, 0, layerWidth, layerHeight };
//        int nonDraw = 0;
//        if ((toolActive == TOOLS_SELECT) || (toolActive == TOOLS_SELECTELLIPSE) ||
//            (toolActive == TOOLS_DROPPER) || (toolActive == TOOLS_WAND) ||
//            (toolActive == TOOLS_SELECTDRAG)) { start_func
//            nonDraw = 1;
//        }
//
//        // So that undo warning boxes don't cancel us!
//        int tool = toolActive;
//        if (lastTime) toolActive = 0;
//    
//        try { // @TODO: Try block not technically needed until undo functions work
//            switch (tool) { start_func
//                case TOOLS_SELECTDRAG:
//                    // Dirty current selection area
//                    setDirtyBox(selectRect.x + selectionXOffs, selectRect.y + selectionYOffs,
//                                selectRect.x + selectRect.w - 1 + selectionXOffs,
//                                selectRect.y + selectRect.h - 1 + selectionYOffs);
//                
//                    // Float selection?
//                    if (firstTime) { start_func
//                        floatSelection();
//                    }
//                    else {
//                        // Move selection
//                        selectionXOffs += virtualCursorX - toolLastX;
//                        selectionYOffs += virtualCursorY - toolLastY;
//                        
//                        // Dirty new area also
//                        setDirtyBox(selectRect.x + selectionXOffs, selectRect.y + selectionYOffs,
//                                    selectRect.x + selectRect.w - 1 + selectionXOffs,
//                                    selectRect.y + selectRect.h - 1 + selectionYOffs);
//                    }
//                    break;
//                    
//                case TOOLS_WAND:
//                case TOOLS_SELECT:
//                case TOOLS_SELECTELLIPSE:
//                    // Keep a backup copy of old selection
//                    if (firstTime) { start_func
//                        memcpy(backupSelected, selected, selectedPitch * layerHeight);
//                        backupSelectRect = selectRect;
//                    }
//                    // Refresh from backup if dragging with ctrl/alt
//                    else if ((toolCtrl) || (toolAlt)) { start_func
//                        memcpy(selected, backupSelected, selectedPitch * layerHeight);
//                        selectRect = backupSelectRect;
//                    }
//                    
//                    if (tool == TOOLS_WAND) { start_func
//                        // We get rectangle from previous time
//                        if (!firstTime) { start_func
//                            rect.x = toolMinX;
//                            rect.y = toolMinY;
//                            rect.w = toolMaxX - toolMinX + 1;
//                            rect.h = toolMaxY - toolMinY + 1;
//    
//                            // Mark previous rect dirty
//                            setDirtyRect(rect, 1);
//                        }
//                        else {
//                            rect.w = 0;
//                        }
//                    }
//                    else if (tool == TOOLS_SELECT) { start_func
//                        rect = createRect(toolStartX, toolStartY, virtualCursorX, virtualCursorY);
//                    }
//                    else {
//                        rX = abs(toolStartX - virtualCursorX);
//                        rY = abs(toolStartY - virtualCursorY);
//                    
//                        // Special case
//                        if (rX < 1) rX = 1;
//                        if (rY < 1) rY = 1;
//    
//                        rect = createRect(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
//                    }
//    
//                    // We can't have a selection rect that's bigger than the selection surface
//                    intersectRects(rect, layerBound);
//    
//                    // Undo?
//                    if (lastTime) { start_func
//                        // If we're clearing selection, we must do extra work here
//                        if ((!toolCtrl) && (!toolAlt)) { start_func
//                            // Refresh from backup
//                            memcpy(selected, backupSelected, selectedPitch * layerHeight);
//                            selectRect = backupSelectRect;
//                            // Add into rectangle the area we're selecting
//                            boundRects(selectRect, rect);
//                            // Clear it AND store undo
//                            clearSelection();
//                        }
//                        else {
//                            // Just undo the area we're adding/deleting from
//                            if (rect.w) undoStoreSelect(rect.x, rect.y, rect.w, rect.h);
//                        }
//                    }
//                    // Clear selection if no ctrl/alt
//                    else if ((!toolCtrl) && (!toolAlt)) clearSelection(0);                
//                    
//                    // Draw
//                    if (tool == TOOLS_WAND) { start_func
//                        // @TODO:
////                        if (!toolContiguous) { start_func
////                            rect = floodFillNonContiguous32(tile, selection, virtualCursorX, virtualCursorY, mapColor32(0, 0, 0, toolAlt ? 0 : 255), toolTolerance);
////                        }
////                        else {
////                            // Use alpha workspace as temp area, use 255/255/255 as filler color
////                            drawRect(0, 0, alphaWorkspace->w, alphaWorkspace->h, mapColor32(255, 255, 255, 255), alphaWorkspace);
////                            rect = floodFill32(tile, alphaWorkspace, virtualCursorX, virtualCursorY, mapColor32(0, 0, 0, toolAlt ? 0 : 255), toolTolerance);
////                            SDL_SetAlpha(alphaWorkspace, 0, 255);
////                            SDL_SetColorKey(alphaWorkspace, SDL_SRCCOLORKEY, mapColor32(255, 255, 255, 255));
////                            blit(0, 0, alphaWorkspace, 0, 0, selection, tileWidth, tileHeight);
////                            SDL_SetColorKey(alphaWorkspace, 0, 0);
////                            SDL_SetAlpha(alphaWorkspace, SDL_SRCALPHA, 255);
////                        }
//    
//                        // Remember min/max for undo next time
//                        if (rect.w) { start_func
//                            toolMinX = rect.x;
//                            toolMinY = rect.y;
//                            toolMaxX = rect.x + rect.w - 1;
//                            toolMaxY = rect.y + rect.h - 1;
//                        }
//                        else {
//                            // Causes no undo area or undirty to occur next frame
//                            toolMinX = toolMaxX = -1;
//                        }
//                    }
//                    else if (tool == TOOLS_SELECT) { start_func
//                        selectRect(toolStartX, toolStartY, virtualCursorX, virtualCursorY, toolAlt ? 0 : 1);
//                    }
//                    else {
//                        selectEllipse(toolStartX, toolStartY, rX, rY, toolAlt ? 0 : 1);
//                    }
//                    setDirtyRect(rect, 1);
//    
//                    // Add to overall selection bounding box
//                    if (!toolAlt) boundRects(selectRect, rect);
//                    // ...or fix selection rectangle if removing stuff
//                    else fixSelectionRect();
//    
//                    // Previous
//                    if (!firstTime) { start_func
//                        if (tool == TOOLS_WAND) { start_func
//                            // (was dirtied above, at beginning)
//                        }
//                        else if (tool == TOOLS_SELECT) { start_func
//                            setDirtyBox(toolStartX, toolStartY, toolLastX, toolLastY, 1);
//                        }
//                        else {
//                            rX = abs(toolStartX - toolLastX);
//                            rY = abs(toolStartY - toolLastY);
//                            if (rX < 1) rX = 1;
//                            if (rY < 1) rY = 1;
//                            setDirtyBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY, 1);
//                        }
//                    }
//                    
//                    // Allow copy now?
//                    if (lastTime) { start_func
//                        if (selectRect.w) tools->findWidget(AnimGroupPaintTools::ID_COPY)->enable();
//                        else tools->findWidget(AnimGroupPaintTools::ID_COPY)->disable();
//                    }
//                    break;
//    
//                case TOOLS_DROPPER:
//                    // @TODO:
//                    break;
//            
//                case TOOLS_PEN:
//                    if (!lastTime) { start_func
//                        layerDrawLine(toolStartX, toolStartY, virtualCursorX, virtualCursorY, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                        if (layerEdit[cursorLayer][1]) layerDrawLine(toolStartX, toolStartY, virtualCursorX, virtualCursorY, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                        setDirtyBox(toolStartX, toolStartY, virtualCursorX, virtualCursorY);
//                    }
//                    
//                    // Track area covered for undo
//                    if (virtualCursorX < toolMinX) toolMinX = virtualCursorX;
//                    if (virtualCursorX > toolMaxX) toolMaxX = virtualCursorX;
//                    if (virtualCursorY < toolMinY) toolMinY = virtualCursorY;
//                    if (virtualCursorY > toolMaxY) toolMaxY = virtualCursorY;
//                    if (toolStartX < toolMinX) toolMinX = toolStartX;
//                    if (toolStartX > toolMaxX) toolMaxX = toolStartX;
//                    if (toolStartY < toolMinY) toolMinY = toolStartY;
//                    if (toolStartY > toolMaxY) toolMaxY = toolStartY;
//    
//                    toolStartX = virtualCursorX;
//                    toolStartY = virtualCursorY;
//    
//                    if (lastTime) undoStoreLayerBox(toolMinX, toolMinY, toolMaxX, toolMaxY);
//                    else if (tools) tools->previewRedraw();
//                    break;
//                    
//                case TOOLS_FILL:
//                    // Undo based on rectangle from last time
//                    if (lastTime) undoStoreLayerBox(toolMinX, toolMinY, toolMaxX, toolMaxY);
//    
//                    // Dirty previous rectangle
//                    if (!firstTime) { start_func
//                        refreshData(1, toolMinX, toolMinY, toolMaxX, toolMaxY);
//                        setDirtyBox(toolMinX, toolMinY, toolMaxX, toolMaxY);
//                    }
//                    
//                    // Flood fill
//                    if (!toolContiguous) { start_func
//                        if (layerEdit[cursorLayer][1]) rect = layerMatchFill(virtualCursorX, virtualCursorY, Layer::LAYER_TILE_FILL_MASK, layerEdit[cursorLayer][0], toolDataMask, toolData, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                        else rect = layerMatchFill(virtualCursorX, virtualCursorY, Layer::LAYER_TILE_FILL_MASK, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                    }
//                    else {
//                        if (layerEdit[cursorLayer][1]) rect = layerFloodFill(virtualCursorX, virtualCursorY, Layer::LAYER_TILE_FILL_MASK, layerEdit[cursorLayer][0], toolDataMask, toolData, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                        else rect = layerFloodFill(virtualCursorX, virtualCursorY, Layer::LAYER_TILE_FILL_MASK, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                    }
//                    setDirtyRect(rect);
//                    
//                    // Remember min/max for undo on last time
//                    if (rect.w) { start_func
//                        toolMinX = rect.x;
//                        toolMinY = rect.y;
//                        toolMaxX = rect.x + rect.w - 1;
//                        toolMaxY = rect.y + rect.h - 1;
//                    }
//                    else {
//                        // Causes no undo area or undirty to occur next frame
//                        toolMinX = toolMaxX = -1;
//                    }
//                    break;
//                    
//                case TOOLS_LINE:
//                case TOOLS_RECT:
//                case TOOLS_RECTFILL:
//                    if (lastTime) undoStoreLayerBox(toolStartX, toolStartY, virtualCursorX, virtualCursorY);
//                    
//                    // Reload from last time, not entire layer
//                    if (!firstTime) refreshData(1, toolStartX, toolStartY, toolLastX, toolLastY);
//    
//                    rX = virtualCursorX;
//                    rY = virtualCursorY;
//                    
//                    // Limit to square or straight line?
//                    if (toolCtrl) { start_func
//                        if (tool == TOOLS_LINE) { start_func
//                            // Determine approximate slope of line
//                            slope1 = abs(toolStartX - rX);
//                            slope2 = abs(toolStartY - rY);
//                            // (we only care, if both sizes are > 0)
//                            if ((slope1) && (slope2)) { start_func
//                                if (slope1 > slope2) swap(slope1, slope2);
//                                // slope1/slope2 will be a fraction between 0 (flat) and
//                                // 1 (diagonal of 45deg multiple); cutoff point is 0.5
//                                if (slope1 * 2 / slope2 >= 1) { start_func
//                                    // Square
//                                    if (abs(toolStartX - rX) < abs(toolStartY - rY)) { start_func
//                                        rY = toolStartY + abs(toolStartX - rX) * (toolStartY < rY ? 1 : -1);
//                                    }
//                                    else {
//                                        rX = toolStartX + abs(toolStartY - rY) * (toolStartX < rX ? 1 : -1);
//                                    }
//                                }
//                                else {
//                                    // Flat line
//                                    if (abs(toolStartX - rX) < abs(toolStartY - rY)) { start_func
//                                        rX = toolStartX;
//                                    }
//                                    else {
//                                        rY = toolStartY;
//                                    }
//                                }
//                            }
//                        }
//                        else {
//                            // Square
//                            if (abs(toolStartX - rX) < abs(toolStartY - rY)) { start_func
//                                rY = toolStartY + abs(toolStartX - rX) * (toolStartY < rY ? 1 : -1);
//                            }
//                            else {
//                                rX = toolStartX + abs(toolStartY - rY) * (toolStartX < rX ? 1 : -1);
//                            }
//                        }
//                    }
//                    
//                    if (tool == TOOLS_LINE) { start_func
//                        layerDrawLine(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                        if (layerEdit[cursorLayer][1]) layerDrawLine(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                    }
//                    else if (tool == TOOLS_RECT) { start_func
//                        layerDrawBox(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                        if (layerEdit[cursorLayer][1]) layerDrawBox(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                    }
//                    else {
//                        layerDrawRect(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                        if (layerEdit[cursorLayer][1]) layerDrawRect(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                    }
//                    setDirtyBox(toolStartX, toolStartY, rX, rY);
//                    // (no need to limit based on toolCtrl, as a limited area will always
//                    //  be smaller than this area we use here)
//                    if (!firstTime) setDirtyBox(toolStartX, toolStartY, toolLastX, toolLastY);
//                    break;
//                    
//                case TOOLS_ELLIPSE:
//                case TOOLS_ELLIPSEFILL:
//                    rX = abs(toolStartX - virtualCursorX);
//                    rY = abs(toolStartY - virtualCursorY);
//                
//                    // Special case
//                    if (rX < 1) rX = 1;
//                    if (rY < 1) rY = 1;
//                    
//                    // Circle?
//                    if (toolCtrl) { start_func
//                        rX = min(rX, rY);
//                        rY = rX;
//                    }
//    
//                    if (lastTime) undoStoreLayerBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
//                    // reload from area affected last time, not entire layer
//                    int prX, prY;
//                    if (!firstTime) { start_func
//                        prX = abs(toolStartX - toolLastX);
//                        prY = abs(toolStartY - toolLastY);
//                        if (prX < 1) prX = 1;
//                        if (prY < 1) prY = 1;
//                        // (no need to limit to a circle based on toolCtrl, this will always
//                        // cover the minimum area needed)
//                        refreshData(1, toolStartX - prX, toolStartY - prY, toolStartX + prX, toolStartY + prY);
//                    }
//    
//                    if (tool == TOOLS_ELLIPSE) { start_func
//                        layerDrawEllipse(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                        if (layerEdit[cursorLayer][1]) layerDrawEllipse(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                    }
//                    else {
//                        layerDrawEllipseFill(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][0], toolDataMask, toolData);
//                        if (layerEdit[cursorLayer][1]) layerDrawEllipseFill(toolStartX, toolStartY, rX, rY, layerEdit[cursorLayer][1], toolFxMask, toolFx);
//                    }
//                    
//                    setDirtyBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
//    
//                    // Previous
//                    if (!firstTime) { start_func
//                        setDirtyBox(toolStartX - prX, toolStartY - prY, toolStartX + prX, toolStartY + prY);
//                    }
//                    break;
//            }
//        }
//        catch (UndoException& e) { start_func
//            toolActive = tool;
//            cancelTool();
//            return;
//        }
//        
//        if ((lastTime) && (!nonDraw)) { start_func
//            // @TODO: only apply a section (to reduce overhead on preview updates)
//            applyData(1);
//        }
//        else if (!nonDraw) tools->previewRedraw();
//        
//        toolLastX = virtualCursorX;
//        toolLastY = virtualCursorY;        
    }
}

void AnimGroupPaint::cancelTool() { start_func
    if (toolActive) {
        switch (toolActive) {
            // @TODO:
            default:
                break;
        }
    
        mousePointer();
        toolActive = 0;
    }
}

void AnimGroupPaint::finishTool() { start_func
    if (toolActive) {
        dragTool(toolLastX, toolLastY, 0, 1);
        mousePointer();
        // toolActive = 0 taken care of by dragtool
        // @TODO: verify the above statement
    }
}

void AnimGroupPaint::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
    assert(destSurface);

    if (visible) {
        // If dirty, redraw range or all
        if (dirty) {
            if (totalDirty) {
                // Range should include entire area, so we cover "outside" current
                // animation if appropriate
                toDisplay = clipArea;
            }
            else {
                dirtyRange.x += x + xOffset;
                dirtyRange.y += y + yOffset;
                // Range must include requested update area as well
                boundRects(toDisplay, dirtyRange);
            }
            dirty = totalDirty = 0;
            dirtyRange.w = 0;
            intersectRects(toDisplay, clipArea);
        }
        
        xOffset += x;
        yOffset += y;
        
        // Anything to draw?
        if (toDisplay.w) {
            SDL_SetClipRect(destSurface, &toDisplay);
            SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_BKFILL]);
            
            // Border
            // @TODO: top/left border
            drawVLine(yOffset, yOffset + anim.height, xOffset + anim.width, guiPacked[COLOR_GRID], destSurface);
            drawHLine(xOffset, xOffset + anim.width, yOffset + anim.height, guiPacked[COLOR_GRID], destSurface);
            drawRect(xOffset + anim.width, yOffset + anim.height, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, guiPacked[COLOR_GRID], destSurface);
            
            // @TODO: Draw all visible components, bottom up
            //        with outlines
            //        with cursor glow
            //        with selection outlines
            // @TODO: Draw current selection tool outline
        }
    }
}


void AnimGroupPaint::undoStore() throw_Undo { start_func
    // @TODO: (check exceptions too)
}

void AnimGroupPaint::undoStoreSelect() throw_Undo { start_func
    // @TODO: (check exceptions too)
}

void AnimGroupPaint::clearSelection(int storeUndo) throw_Undo { start_func
    // @TODO: (check exceptions too)
    if (selectRect.w) {
        // @TODO:
        if (storeUndo) ;
        
        setDirtyRect(selectRect);
        selected.clear();
        selectRect.w = 0;
        tools->findWidget(AnimGroupPaintTools::ID_COPY)->disable();
    }
}

void AnimGroupPaint::pasteSelection() throw_Undo { start_func
    // @TODO: (check exceptions too)
    // @TODO:
    // if (canConvertClipboard(CLIPBOARD_)) {
    // }
}

void AnimGroupPaint::copySelection() { start_func
    if (selectRect.w) {
        // @TODO:
    }
}

void AnimGroupPaint::deleteSelection() throw_Undo { start_func
    // @TODO: (check exceptions too)
}

int AnimGroupPaint::isInSelection(int x, int y) const { start_func
    if (!selectRect.w) return 0;

    // @TODO:
    return 0;
}

void AnimGroupPaint::mousePointer(int mouseX, int mouseY) { start_func
    lastMouseX = mouseX;
    lastMouseY = mouseY;
    mousePointer();
}

void AnimGroupPaint::mousePointer() { start_func
    if (!hover) return;

    if ((lastMouseX > anim.width) && (lastMouseX < anim.width + RESIZE_HANDLE_SIZE) &&
        (lastMouseY > anim.height) && (lastMouseY < anim.height + RESIZE_HANDLE_SIZE)) {
        selectMouse(MOUSE_DIAGDOWN);
        return;
    }
    if ((toolActive == TOOLS_SELECT) || (toolActive == TOOLS_SELECTITEM)) {
        if (toolAlt) {
            selectMouse(MOUSE_SUBTRACT);
            return;
        }
        else if (toolCtrl) {
            selectMouse(MOUSE_ADD);
            return;
        }
    }
    if (toolActive == TOOLS_SELECTDRAG) {
        selectMouse(MOUSE_FOURDIRECTION);
        return;
    }
    if ((!toolActive) && ((toolL == TOOLS_SELECT) || (toolL == TOOLS_SELECTITEM))) {
        if (SDL_GetModState() & KMOD_ALT) {
            selectMouse(MOUSE_SUBTRACT);
            return;
        }
        if (SDL_GetModState() & KMOD_CTRL) {
            selectMouse(MOUSE_ADD);
            return;
        }
        
        if (isInSelection(lastMouseX, lastMouseY)) {
            selectMouse(MOUSE_FOURDIRECTION);
            return;
        }
    }    
        
    selectMouse(MOUSE_NORMAL);
}

Window::CommandSupport AnimGroupPaint::supportsCommand(int code) const { start_func
    switch (code) {
        // These are always available
        case TOOLS_CHOOSE:
        case TOOLS_CHOOSER:
        case TOOLS_SELECT:
        case TOOLS_SELECTITEM:
        case TOOLS_PLACEITEM:
        case VIEW_OUTLINE_CHOOSE:
        case VIEW_OUTLINE_NONE:
        case VIEW_OUTLINE_NORMAL:
        case VIEW_OUTLINE_HANDLE:
        case EDIT_SELECTALL:
        case TOOLS_SETTINGS:
        case TOOLS_EDITCOLOR:
        case TOOLS_NEXTCOLOR:
        case TOOLS_PREVCOLOR:
            return Window::COMMAND_ENABLE;
            
        case EDIT_COPY:
        case EDIT_CUT:
        case EDIT_DELETE:
        case EDIT_DESELECTALL:
            if (selectRect.w) return Window::COMMAND_ENABLE;
            else return Window::COMMAND_DISABLE;
        
        case EDIT_PASTE:
            // @TODO: if (canConvertClipboard(CLIPBOARD_)) return Window::COMMAND_ENABLE;
            // @TODO: else
            return Window::COMMAND_DISABLE;
    }
    
    return animgroup->getWorldEdit()->supportsCommand(code);
}

void AnimGroupPaint::refresh() { start_func
    setDirty(1);
}

Window::WindowType AnimGroupPaint::windowType() const { start_func
    return WINDOW_CLIENT;
}

